package jinyilw.swing.rlaf;

import jinyilw.common.SettingTools;
import jinyilw.common.app.AppConfig;
import jinyilw.common.file.FileTools;
import jinyilw.swing.ResourceManager;
import jinyilw.swing.image.ImageTools;
import jinyilw.swing.rlaf.theme.RRootPaneSet;
import jinyilw.swing.rlaf.ui.RLookAndFeel;
import jinyilw.swing.rlaf.utils.RLafUtils;

import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JPopupMenu;
import javax.swing.LookAndFeel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Frame;
import java.awt.Image;
import java.awt.Window;
import java.io.File;
import java.util.ArrayList;

/**
 * @author Jinyi
 */
public class UITools
{
	static ArrayList<UISettable> uiSettables = new ArrayList<>();
	public static final int lightTheme = 0;
	public static final int darkTheme = 1;
	public static final int blackTheme = 2;
	public static int themeType;
	public static int imageBlurriness;
	public static Color lightBGColor, darkBGColor, blackBGColor;

	public static final int bgWidth = 1280, bgHeight = 720;
	public static Image lightImage, darkImage, blackImage;
	private static Image smallBlurLightImage;
	private static File lightBGFile, darkBGFile, blackBGFile;
	private static Image lightSourceImage, darkSourceImage, blackSourceImage;

	private static final String customBG = ".customBG";
	private static File customBGFolder, lightCustomFile, darkCustomFile,
			blackCustomFile;

	private static volatile RLookAndFeel rLookAndFeel;

	private UITools()
	{
	}

	private static void init()
	{
		lightBGColor = new Color(255, 255, 255, 70);
		darkBGColor = new Color(0, 0, 0, 170);
		blackBGColor = new Color(0, 0, 0, 220);
		themeType = SettingTools.getDefaultInt("themeType", 1);
		imageBlurriness = SettingTools.getDefaultInt("imageBlurriness", 0);
		System.out.println("imageBlurriness:" + imageBlurriness);
		customBGFolder = FileTools.createHiddenDirectory(customBG);
		lightCustomFile = new File(customBGFolder, "light");
		darkCustomFile = new File(customBGFolder, "dark");
		blackCustomFile = new File(customBGFolder, "black");

		switch (themeType)
		{
			case lightTheme:
				initLightBG();
				new Thread(() -> {
					initDarkBG();
					initBlackBG();
				}).start();
				break;
			case darkTheme:
				initDarkBG();
				new Thread(() -> {
					initLightBG();
					initBlackBG();
				}).start();
				break;
			case blackTheme:
				initBlackBG();
				new Thread(() -> {
					initLightBG();
					initDarkBG();
				}).start();
		}

		AppConfig.addAppExitedOperator(o -> {
			if (lightBGFile == null && darkBGFile == null
					&& blackBGFile == null)
				return;
			if (!customBGFolder.exists())
			{
				customBGFolder.mkdirs();
				FileTools.setToHidden(customBGFolder);
			}
			if (lightBGFile != null)
				FileTools.copyFile(lightBGFile, lightCustomFile);
			if (darkBGFile != null)
				FileTools.copyFile(darkBGFile, darkCustomFile);
			if (blackBGFile != null)
				FileTools.copyFile(blackBGFile, blackCustomFile);
		});
	}

	private static void initLightBG()
	{
		lightSourceImage = ResourceManager.getBufferedImage(lightCustomFile);
		if (lightSourceImage == null)
			lightSourceImage = ResourceManager.getBufferedImage(
					FileTools.getRandomFileInDirectory("bgImages"));
		if (lightSourceImage == null)
		{
			lightSourceImage = RRootPaneSet.getDefaultLightImage();
		} else
			lightSourceImage = getCustomOriginalBGImage(lightSourceImage, lightTheme);
		lightImage = ImageTools.getBlurImage(lightSourceImage, imageBlurriness);
	}

	private static void initDarkBG()
	{
		darkSourceImage = ResourceManager.getBufferedImage(darkCustomFile);
		if (darkSourceImage == null)
			darkSourceImage = RRootPaneSet.getDefaultDarkImage();
		else
			darkSourceImage = getCustomOriginalBGImage(darkSourceImage, darkTheme);
		darkImage = ImageTools.getBlurImage(darkSourceImage, imageBlurriness);
	}

	private static void initBlackBG()
	{
		blackSourceImage = ResourceManager.getBufferedImage(blackCustomFile);
		if (blackSourceImage == null)
			blackSourceImage = RRootPaneSet.getDefaultBlackImage();
		else
			blackSourceImage = getCustomOriginalBGImage(blackSourceImage, blackTheme);
		blackImage = ImageTools.getBlurImage(blackSourceImage, imageBlurriness);
	}

	public static void initRLookAndFeel()
	{
		if (rLookAndFeel == null)
			synchronized (RLookAndFeel.class)
			{
				if (rLookAndFeel == null)
				{
					init();
					JFrame.setDefaultLookAndFeelDecorated(true);
					JDialog.setDefaultLookAndFeelDecorated(true);
					rLookAndFeel = new RLookAndFeel();
					try
					{
						UIManager.setLookAndFeel(rLookAndFeel);
					} catch (Throwable e)
					{
						e.printStackTrace();
					}
				}
			}
	}

	public static void installUITheme(UISettable uiSettable)
	{
		switch (themeType)
		{
			case lightTheme:
				uiSettable.lightTheme();
				break;
			case darkTheme:
				uiSettable.darkTheme();
				break;
			case blackTheme:
				uiSettable.blackTheme();
		}
		uiSettables.add(uiSettable);
	}

	public static void initTheme(UISettable uiSettable)
	{
		switch (themeType)
		{
			case lightTheme:
				uiSettable.lightTheme();
				break;
			case darkTheme:
				uiSettable.darkTheme();
				break;
			case blackTheme:
				uiSettable.blackTheme();
		}
	}

	public static void setLightTheme()
	{
		if (themeType == lightTheme)
			return;
		themeType = lightTheme;
		for (UISettable uiSettable : uiSettables)
			uiSettable.lightTheme();
		update();
	}

	public static void setDarkTheme()
	{
		if (themeType == darkTheme)
			return;
		themeType = darkTheme;
		for (UISettable uiSettable : uiSettables)
			uiSettable.darkTheme();
		update();
	}

	public static void setBlackTheme()
	{
		if (themeType == blackTheme)
			return;
		themeType = blackTheme;
		for (UISettable uiSettable : uiSettables)
			uiSettable.blackTheme();
		update();
	}

	public static Image getColoredImage(Image image, int type)
	{
		Color bgColor =
				type == lightTheme ? lightBGColor : type == darkTheme ? darkBGColor : blackBGColor;
		return ImageTools.getColoredImage(image, bgColor);
	}

	public static void setImageBlurriness(int blurriness)
	{
		if (imageBlurriness == blurriness || blurriness < 0 || blurriness > 100)
			return;
		imageBlurriness = blurriness;
		switch (themeType)
		{
			case lightTheme:
				lightImage = ImageTools.getBlurImage(lightSourceImage, imageBlurriness);
				RRootPaneSet.getRootPaneSet().lightTheme();
				break;
			case darkTheme:
				darkImage = ImageTools.getBlurImage(darkSourceImage, imageBlurriness);
				RRootPaneSet.getRootPaneSet().darkTheme();
				break;
			case blackTheme:
				blackImage = ImageTools.getBlurImage(blackSourceImage, imageBlurriness);
				RRootPaneSet.getRootPaneSet().blackTheme();
		}
		for (Window w : Window.getWindows())
			if (w.isVisible())
				w.repaint();
		new Thread(() -> {
			switch (themeType)
			{
				case lightTheme:
					darkImage = ImageTools.getBlurImage(darkSourceImage, imageBlurriness);
					blackImage = ImageTools.getBlurImage(blackSourceImage, imageBlurriness);
					break;
				case darkTheme:
					lightImage = ImageTools.getBlurImage(lightSourceImage, imageBlurriness);
					blackImage = ImageTools.getBlurImage(blackSourceImage, imageBlurriness);
					break;
				case blackTheme:
					lightImage = ImageTools.getBlurImage(lightSourceImage, imageBlurriness);
					darkImage = ImageTools.getBlurImage(darkSourceImage, imageBlurriness);
			}
		}).start();
	}

	public static void setBGImageFile(File file)
	{
		Image image = ResourceManager.getBufferedImage(file);
		if (image == null)
			return;
		switch (themeType)
		{
			case lightTheme:
				lightBGFile = file;
				lightSourceImage = getCustomOriginalBGImage(image, lightTheme);
				lightImage = ImageTools.getBlurImage(lightSourceImage, imageBlurriness);
				RRootPaneSet.getRootPaneSet().lightTheme();
				break;
			case darkTheme:
				darkBGFile = file;
				darkSourceImage = getCustomOriginalBGImage(image, darkTheme);
				darkImage = ImageTools.getBlurImage(darkSourceImage, imageBlurriness);
				RRootPaneSet.getRootPaneSet().darkTheme();
				break;
			case blackTheme:
				blackBGFile = file;
				blackSourceImage = getCustomOriginalBGImage(image, blackTheme);
				blackImage = ImageTools.getBlurImage(blackSourceImage, imageBlurriness);
				RRootPaneSet.getRootPaneSet().blackTheme();
		}
		for (Window w : Window.getWindows())
			if (w.isVisible())
				w.repaint();
	}

	private static Image getCustomOriginalBGImage(Image image, int type)
	{
		int width = image.getWidth(null), height = image.getHeight(null);
		if (width * height > bgWidth * bgHeight)
		{
			double dw = width / (double) bgWidth,
					dh = height / (double) bgHeight;
			if (dw > dh)
			{
				width = bgWidth;
				height = (int) (height / dw);
			} else
			{
				width = (int) (width / dh);
				height = bgHeight;
			}
		}
		Image img = ImageTools.getShapedImage(image, width, height);
		switch (type)
		{
			case lightTheme:
				return ImageTools.getColoredImage(img, lightBGColor);
			case darkTheme:
				return ImageTools.getColoredImage(img, darkBGColor);
			default:
				return ImageTools.getColoredImage(img, blackBGColor);
		}
	}

	public static Image getSmallBlurLightImage()
	{
		if (smallBlurLightImage == null)
			smallBlurLightImage = ImageTools.getShapedBlurImage(lightImage,
					lightImage.getWidth(null) / 4,
					lightImage.getHeight(null) / 4, 20);
		return smallBlurLightImage;
	}

	private static void getLightImage(Image img)
	{
		//		int gray = PixelsTools.getGrayValue(img);
		//		System.out.println(gray);
		//		int bright = gray < 220 ? 220 - gray : 10;
		//		if (bright > 255)
		//			bright = 255;
		//		System.out.println(bright);
		lightImage = ImageTools.getColoredImage(img,
				lightBGColor);
	}

	public static void resetBGImage()
	{
		switch (themeType)
		{
			case lightTheme:
				lightSourceImage = lightImage = RRootPaneSet.getDefaultLightImage();
				lightBGFile = null;
				RRootPaneSet.getRootPaneSet().lightTheme();
				lightCustomFile.delete();
				break;
			case darkTheme:
				darkSourceImage = darkImage = RRootPaneSet.getDefaultDarkImage();
				darkBGFile = null;
				RRootPaneSet.getRootPaneSet().darkTheme();
				darkCustomFile.delete();
				break;
			case blackTheme:
				blackSourceImage = blackImage = RRootPaneSet.getDefaultBlackImage();
				blackBGFile = null;
				RRootPaneSet.getRootPaneSet().blackTheme();
				blackCustomFile.delete();
		}
		for (Window w : Window.getWindows())
			if (w.isVisible())
				w.repaint();
		imageBlurriness = 0;
		RLafUtils.setImageBlurriness(0);
	}

	static void repaintComponent(Component c)
	{
		c.repaint();
		Component[] children = null;
		if (c instanceof Container)
			children = ((Container) c).getComponents();
		if (children != null)
			for (Component child : children)
				repaintComponent(child);
	}

	public static void update()
	{
		RLafUtils.updateWindowBackground();
		for (Window w : Window.getWindows())
			if (w.isVisible())
				w.repaint();
	}

	public static boolean isLight()
	{
		return themeType == lightTheme;
	}

	public static boolean isNotLight()
	{
		return themeType != lightTheme;
	}

	public static boolean isDark()
	{
		return themeType == darkTheme;
	}

	public static boolean isBlack()
	{
		return themeType == blackTheme;
	}

	public static Color chooseColor(Color lightColor, Color darkColor,
			Color blackColor)
	{
		if (themeType == lightTheme)
			return lightColor;
		if (themeType == darkTheme)
			return darkColor;
		return blackColor;
	}

	public static void uninstallTheme(Component c)
	{
		if (c instanceof UISettable)
			uiSettables.remove(c);
		if (c instanceof JComponent)
		{
			JComponent jc = (JComponent) c;
			JPopupMenu jpm = jc.getComponentPopupMenu();
			if (jpm != null)
				uninstallTheme(jpm);
		}
		Component[] children = null;
		if (c instanceof JMenu)
			children = ((JMenu) c).getMenuComponents();
		else if (c instanceof Container)
			children = ((Container) c).getComponents();
		if (children != null)
			for (Component child : children)
				uninstallTheme(child);
	}

	public static void uninstall(UISettable uiSettable)
	{
		uiSettables.remove(uiSettable);
	}

	public static void updateLookAndFeel(LookAndFeel lookAndFeel)
	{
		try
		{
			UIManager.setLookAndFeel(lookAndFeel);
		} catch (Exception ex)
		{
			ex.printStackTrace();
		}
		for (Frame f : Frame.getFrames())
			SwingUtilities.updateComponentTreeUI(f);

		for (Window w : Window.getWindows())
			SwingUtilities.updateComponentTreeUI(w);
	}

	public static void updateLookAndFeel(Frame frame, LookAndFeel lookAndFeel)
	{
		try
		{
			UIManager.setLookAndFeel(lookAndFeel);
		} catch (Exception ex)
		{
			ex.printStackTrace();
		}
		SwingUtilities.updateComponentTreeUI(frame);
		for (Window w : frame.getOwnedWindows())
			SwingUtilities.updateComponentTreeUI(w);
		for (Window w : Window.getOwnerlessWindows())
			SwingUtilities.updateComponentTreeUI(w);
	}
}
